home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / DefaultTreeModel.java < prev    next >
Text File  |  1998-06-30  |  18KB  |  484 lines

  1. /*
  2.  * @(#)DefaultTreeModel.java    1.29 98/04/02
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.tree;
  22.  
  23. import java.util.*;
  24. import java.awt.*;
  25. import java.io.*;
  26. import com.sun.java.swing.event.*;
  27.  
  28. /**
  29.  * A simple tree data model that uses TreeNodes.
  30.  * <p>
  31.  * Warning: serialized objects of this class will not be compatible with
  32.  * future swing releases.  The current serialization support is appropriate
  33.  * for short term storage or RMI between Swing1.0 applications.  It will
  34.  * not be possible to load serialized Swing1.0 objects with future releases
  35.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  36.  * baseline for the serialized form of Swing objects.
  37.  *
  38.  * @version 1.29 04/02/98
  39.  * @author Rob Davis
  40.  * @author Ray Ryan
  41.  * @author Scott Violet
  42.  */
  43. public class DefaultTreeModel implements Serializable, TreeModel {
  44.     /** Root of the tree. */
  45.     protected TreeNode root;
  46.     /** Listeners. */
  47.     protected EventListenerList listenerList = new EventListenerList();
  48.     /**
  49.       * Determines how the <code>isLeaf</code> determines if
  50.       * a node is a leaf node. If true, a node is a leaf 
  51.       * node if it does not allow children. (If it allows 
  52.       * children, it is not a leaf node, even if no children
  53.       * are present.) If this value is false, then any node 
  54.       * which has no children is a leaf node. 
  55.       *
  56.       * @see TreeNode#getAllowsChildren
  57.       * @see TreeModel#isLeaf
  58.       * @see #setAsksAllowsChildren
  59.       */
  60.     protected boolean asksAllowsChildren;
  61.  
  62.     public DefaultTreeModel(TreeNode root) {
  63.         this(root, false);
  64.     }
  65.  
  66.     public DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) {
  67.         super();
  68.         if (root == null) {
  69.             throw new IllegalArgumentException("root is null");
  70.         }
  71.         this.root = root;
  72.         this.asksAllowsChildren = asksAllowsChildren;
  73.     }
  74.  
  75.     /**
  76.       * Sets whether or not to test leafness by asking getAllowsChildren()
  77.       * or isLeaf() to the TreeNodes.  If newvalue is true, getAllowsChildren()
  78.       * is messaged, otherwise isLeaf() is messaged.
  79.       */
  80.     public void setAsksAllowsChildren(boolean newValue) {
  81.         asksAllowsChildren = newValue;
  82.     }
  83.  
  84.     /**
  85.       * Tells how leaf nodes are determined.
  86.       *
  87.       * @return true if only nodes which do not allow children are
  88.       *         leaf nodes, false if nodes which have no children
  89.       *         (even if allowed) are leaf nodes
  90.       * @see #asksAllowsChildren
  91.       */
  92.     public boolean asksAllowsChildren() {
  93.         return asksAllowsChildren;
  94.     }
  95.  
  96.     public Object getRoot() {
  97.         return root;
  98.     }
  99.  
  100.     public int getIndexOfChild(Object parent, Object child) {
  101.         if(parent == null || child == null)
  102.             return 0;
  103.         return ((TreeNode)parent).getIndex((TreeNode)child);
  104.     }
  105.  
  106.     public Object getChild(Object parent, int index) {
  107.         return ((TreeNode)parent).getChildAt(index);
  108.     }
  109.  
  110.     public int getChildCount(Object parent) {
  111.         return ((TreeNode)parent).getChildCount();
  112.     }
  113.  
  114.     /** 
  115.      * Returns whether the specified node is a leaf node.
  116.      * The way the test is performed depends on the
  117.      * <code>askAllowsChildren</code> setting.
  118.      *
  119.      * @param node the node to check
  120.      * @return true if the node is a leaf node
  121.      *
  122.      * @see #asksAllowsChildren
  123.      * @see TreeModel#isLeaf
  124.      */
  125.     public boolean isLeaf(Object node) {
  126.         if(asksAllowsChildren)
  127.             return !((TreeNode)node).getAllowsChildren();
  128.         return ((TreeNode)node).isLeaf();
  129.     }
  130.  
  131.     /**
  132.      * Invoke this method if you've modified the TreeNodes upon which this
  133.      * model depends.  The model will notify all of its listeners that the
  134.      * model has changed.
  135.      */
  136.     public void reload() {
  137.         reload(root);
  138.     }
  139.  
  140.     /**
  141.       * This sets the user object of the TreeNode identified by path
  142.       * and posts a node changed.  If you use custom user objects in
  143.       * the TreeModel you're going to need to subclass this and
  144.       * set the user object of the changed node to something meaningful.
  145.       */
  146.     public void valueForPathChanged(TreePath path, Object newValue) {
  147.         Object[]             aPath = path.getPath();
  148.         MutableTreeNode      aNode = (MutableTreeNode)aPath[aPath.length - 1];
  149.  
  150.         aNode.setUserObject(newValue);
  151.         nodeChanged(aNode);
  152.     }
  153.  
  154.     /**
  155.      * Invoked this to insert newChild at location index in parents children.
  156.      * This will then message nodesWereInserted to create the appropriate
  157.      * event. This is the preferred way to add children as it will create
  158.      * the appropriate event.
  159.      */
  160.     public void insertNodeInto(MutableTreeNode newChild,
  161.                                MutableTreeNode parent, int index){
  162.         parent.insert(newChild, index);
  163.  
  164.         int[]           newIndexs = new int[1];
  165.  
  166.         newIndexs[0] = index;
  167.         nodesWereInserted(parent, newIndexs);
  168.     }
  169.  
  170.     /**
  171.      * Message this to remove node from its parent. This will message
  172.      * nodesWereRemoved to create the appropriate event. This is the
  173.      * preferred way to remove a node as it handles the event creation
  174.      * for you.
  175.      */
  176.     public void removeNodeFromParent(MutableTreeNode node) {
  177.         MutableTreeNode         parent = (MutableTreeNode)node.getParent();
  178.  
  179.         if(parent == null)
  180.             throw new IllegalArgumentException("node does not have a parent.");
  181.  
  182.         int[]            childIndex = new int[1];
  183.         Object[]         removedArray = new Object[1];
  184.  
  185.         childIndex[0] = parent.getIndex(node);
  186.         parent.remove(childIndex[0]);
  187.         removedArray[0] = node;
  188.         nodesWereRemoved(parent, childIndex, removedArray);
  189.     }
  190.  
  191.     /**
  192.       * Invoke this method after you've changed how node is to be
  193.       * represented in the tree.
  194.       */
  195.     public void nodeChanged(TreeNode node) {
  196.         if(listenerList != null && node != null) {
  197.             TreeNode         parent = node.getParent();
  198.  
  199.             if(parent != null) {
  200.                 int        anIndex = parent.getIndex(node);
  201.                 if(anIndex != -1) {
  202.                     int[]        cIndexs = new int[1];
  203.  
  204.                     cIndexs[0] = anIndex;
  205.                     nodesChanged(parent, cIndexs);
  206.                 }
  207.             }
  208.         }
  209.     }
  210.  
  211.     /**
  212.      * Invoke this method if you've modified the TreeNodes upon which this
  213.      * model depends.  The model will notify all of its listeners that the
  214.      * model has changed below the node <code>node</code> (PENDING).
  215.      */
  216.     public void reload(TreeNode node) {
  217.         if(node != null) {
  218.             fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  219.         }
  220.     }
  221.  
  222.     /**
  223.       * Invoke this method after you've inserted some TreeNodes into
  224.       * node.  childIndices should be the index of the new elements and
  225.       * must be sorted in ascending order.
  226.       */
  227.     public void nodesWereInserted(TreeNode node, int[] childIndices) {
  228.         if(listenerList != null && node != null && childIndices != null
  229.            && childIndices.length > 0) {
  230.             int               cCount = childIndices.length;
  231.             Object[]          newChildren = new Object[cCount];
  232.  
  233.             for(int counter = 0; counter < cCount; counter++)
  234.                 newChildren[counter] = node.getChildAt(childIndices[counter]);
  235.             fireTreeNodesInserted(this, getPathToRoot(node), childIndices, 
  236.                                   newChildren);
  237.         }
  238.     }
  239.     
  240.     /**
  241.       * Invoke this method after you've removed some TreeNodes from
  242.       * node.  childIndices should be the index of the removed elements and
  243.       * must be sorted in ascending order. And removedChildren should be
  244.       * the array of the children objects that were removed.
  245.       */
  246.     public void nodesWereRemoved(TreeNode node, int[] childIndices,
  247.                                  Object[] removedChildren) {
  248.         if(node != null && childIndices != null) {
  249.             fireTreeNodesRemoved(this, getPathToRoot(node), childIndices, 
  250.                                  removedChildren);
  251.         }
  252.     }
  253.  
  254.     /**
  255.       * Invoke this method after you've changed how the children identified by
  256.       * childIndicies are to be represented in the tree.
  257.       */
  258.     public void nodesChanged(TreeNode node, int[] childIndices) {
  259.         if(node != null && childIndices != null) {
  260.             int            cCount = childIndices.length;
  261.  
  262.             if(cCount > 0) {
  263.                 Object[]       cChildren = new Object[cCount];
  264.  
  265.                 for(int counter = 0; counter < cCount; counter++)
  266.                     cChildren[counter] = node.getChildAt
  267.                         (childIndices[counter]);
  268.                 fireTreeNodesChanged(this, getPathToRoot(node), childIndices, 
  269.                                      cChildren);
  270.             }
  271.         }
  272.     }
  273.  
  274.     /**
  275.       * Invoke this method if you've totally changed the children of
  276.       * node and its childrens children...  This will post a
  277.       * treeStructureChanged event.
  278.       */
  279.     public void nodeStructureChanged(TreeNode node) {
  280.         if(node != null) {
  281.            fireTreeStructureChanged(this, getPathToRoot(node), null, null);
  282.         }
  283.     }
  284.  
  285.     /**
  286.      * Builds the parents of node up to and including the root node,
  287.      * where the original node is the last element in the returned array.
  288.      * The length of the returned array gives the node's depth in the
  289.      * tree.
  290.      * 
  291.      * @param aNode the TreeNode to get the path for
  292.      * @param an array of TreeNodes giving the path from the root to the
  293.      *        specified node. 
  294.      */
  295.     public TreeNode[] getPathToRoot(TreeNode aNode) {
  296.         return getPathToRoot(aNode, 0);
  297.     }
  298.  
  299.     /**
  300.      * Builds the parents of node up to and including the root node,
  301.      * where the original node is the last element in the returned array.
  302.      * The length of the returned array gives the node's depth in the
  303.      * tree.
  304.      * 
  305.      * @param aNode  the TreeNode to get the path for
  306.      * @param depth  an int giving the number of steps already taken towards
  307.      *        the root (on recursive calls), used to size the returned array
  308.      * @return an array of TreeNodes giving the path from the root to the
  309.      *         specified node 
  310.      */
  311.     protected TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
  312.         TreeNode[]              retNodes;
  313.     // This method recurses, traversing towards the root in order
  314.     // size the array. On the way back, it fills in the nodes,
  315.     // starting from the root and working back to the original node.
  316.  
  317.         /* Check for null, in case someone passed in a null node, or
  318.            they passed in an element that isn't rooted at root. */
  319.         if(aNode == null) {
  320.             if(depth == 0)
  321.                 return null;
  322.             else
  323.                 retNodes = new TreeNode[depth];
  324.         }
  325.         else {
  326.             depth++;
  327.             if(aNode == root)
  328.                 retNodes = new TreeNode[depth];
  329.             else
  330.                 retNodes = getPathToRoot(aNode.getParent(), depth);
  331.             retNodes[retNodes.length - depth] = aNode;
  332.         }
  333.         return retNodes;
  334.     }
  335.  
  336.     //
  337.     //  Events
  338.     //
  339.  
  340.     public void addTreeModelListener(TreeModelListener l) {
  341.         listenerList.add(TreeModelListener.class, l);
  342.     }
  343.  
  344.     public void removeTreeModelListener(TreeModelListener l) {
  345.         listenerList.remove(TreeModelListener.class, l);
  346.     }
  347.  
  348.     /*
  349.      * Notify all listeners that have registered interest for
  350.      * notification on this event type.  The event instance 
  351.      * is lazily created using the parameters passed into 
  352.      * the fire method.
  353.      * @see EventListenerList
  354.      */
  355.     protected void fireTreeNodesChanged(Object source, Object[] path, 
  356.                                         int[] childIndices, 
  357.                                         Object[] children) {
  358.         // Guaranteed to return a non-null array
  359.         Object[] listeners = listenerList.getListenerList();
  360.         TreeModelEvent e = null;
  361.         // Process the listeners last to first, notifying
  362.         // those that are interested in this event
  363.         for (int i = listeners.length-2; i>=0; i-=2) {
  364.             if (listeners[i]==TreeModelListener.class) {
  365.                 // Lazily create the event:
  366.                 if (e == null)
  367.                     e = new TreeModelEvent(source, path, 
  368.                                            childIndices, children);
  369.                 ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
  370.             }          
  371.         }
  372.     }
  373.  
  374.     /*
  375.      * Notify all listeners that have registered interest for
  376.      * notification on this event type.  The event instance 
  377.      * is lazily created using the parameters passed into 
  378.      * the fire method.
  379.      * @see EventListenerList
  380.      */
  381.     protected void fireTreeNodesInserted(Object source, Object[] path, 
  382.                                         int[] childIndices, 
  383.                                         Object[] children) {
  384.         // Guaranteed to return a non-null array
  385.         Object[] listeners = listenerList.getListenerList();
  386.         TreeModelEvent e = null;
  387.         // Process the listeners last to first, notifying
  388.         // those that are interested in this event
  389.         for (int i = listeners.length-2; i>=0; i-=2) {
  390.             if (listeners[i]==TreeModelListener.class) {
  391.                 // Lazily create the event:
  392.                 if (e == null)
  393.                     e = new TreeModelEvent(source, path, 
  394.                                            childIndices, children);
  395.                 ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
  396.             }          
  397.         }
  398.     }
  399.  
  400.     /*
  401.      * Notify all listeners that have registered interest for
  402.      * notification on this event type.  The event instance 
  403.      * is lazily created using the parameters passed into 
  404.      * the fire method.
  405.      * @see EventListenerList
  406.      */
  407.     protected void fireTreeNodesRemoved(Object source, Object[] path, 
  408.                                         int[] childIndices, 
  409.                                         Object[] children) {
  410.         // Guaranteed to return a non-null array
  411.         Object[] listeners = listenerList.getListenerList();
  412.         TreeModelEvent e = null;
  413.         // Process the listeners last to first, notifying
  414.         // those that are interested in this event
  415.         for (int i = listeners.length-2; i>=0; i-=2) {
  416.             if (listeners[i]==TreeModelListener.class) {
  417.                 // Lazily create the event:
  418.                 if (e == null)
  419.                     e = new TreeModelEvent(source, path, 
  420.                                            childIndices, children);
  421.                 ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
  422.             }          
  423.         }
  424.     }
  425.  
  426.     /*
  427.      * Notify all listeners that have registered interest for
  428.      * notification on this event type.  The event instance 
  429.      * is lazily created using the parameters passed into 
  430.      * the fire method.
  431.      * @see EventListenerList
  432.      */
  433.     protected void fireTreeStructureChanged(Object source, Object[] path, 
  434.                                         int[] childIndices, 
  435.                                         Object[] children) {
  436.         // Guaranteed to return a non-null array
  437.         Object[] listeners = listenerList.getListenerList();
  438.         TreeModelEvent e = null;
  439.         // Process the listeners last to first, notifying
  440.         // those that are interested in this event
  441.         for (int i = listeners.length-2; i>=0; i-=2) {
  442.             if (listeners[i]==TreeModelListener.class) {
  443.                 // Lazily create the event:
  444.                 if (e == null)
  445.                     e = new TreeModelEvent(source, path, 
  446.                                            childIndices, children);
  447.                 ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
  448.             }          
  449.         }
  450.     }
  451.  
  452.     // Serialization support.  
  453.     private void writeObject(ObjectOutputStream s) throws IOException {
  454.         Vector      values = new Vector();
  455.  
  456.         s.defaultWriteObject();
  457.         // Save the cellRenderer, if its Serializable.
  458.         if(root != null && root instanceof Serializable) {
  459.             values.addElement("root");
  460.             values.addElement(root);
  461.         }
  462.         s.writeObject(values);
  463.     }
  464.  
  465.     private void readObject(ObjectInputStream s) 
  466.         throws IOException, ClassNotFoundException {
  467.         s.defaultReadObject();
  468.  
  469.         Vector          values = (Vector)s.readObject();
  470.         int             indexCounter = 0;
  471.         int             maxCounter = values.size();
  472.  
  473.         if(indexCounter < maxCounter && values.elementAt(indexCounter).
  474.            equals("root")) {
  475.             root = (TreeNode)values.elementAt(++indexCounter);
  476.             indexCounter++;
  477.         }
  478.     }
  479.  
  480.  
  481. } // End of class DefaultTreeModel
  482.  
  483.  
  484.